# 美观流畅的烟花程序
import tkinter as tk  # 导入图形界面库
import random  # 导入随机数生成库
import math  # 导入数学计算库
import time  # 导入时间处理库

class Particle:
    """烟花爆炸后的粒子类"""
    def __init__(self, x, y, vx, vy, color, life=50):
        # 初始化粒子的位置坐标
        self.x = x
        self.y = y
        # 初始化粒子的速度分量
        self.vx = vx
        self.vy = vy
        # 粒子的颜色
        self.color = color
        # 粒子的生命周期
        self.life = life
        # 粒子的最大生命周期（用于计算大小变化）
        self.max_life = life
        # 重力加速度
        self.gravity = 0.15
        # 空气阻力系数
        self.air_resistance = 0.98
        # 粒子的尾迹点列表
        self.trail = []
        # 尾迹的最大长度
        self.max_trail = 6
        
    def update(self):
        """更新粒子的物理状态"""
        # 添加当前位置到尾迹
        self.trail.append((self.x, self.y))
        # 如果尾迹太长，删除最旧的点
        if len(self.trail) > self.max_trail:
            self.trail.pop(0)
        
        # 物理更新：位置变化
        self.x += self.vx
        self.y += self.vy
        # 重力影响：垂直速度增加
        self.vy += self.gravity
        # 空气阻力：速度逐渐减小
        self.vx *= self.air_resistance
        self.vy *= self.air_resistance
        # 生命周期减少
        self.life -= 1
        
        # 返回粒子是否还活着且在屏幕范围内
        return self.life > 0 and 0 <= self.x < 800 and 0 <= self.y < 600

class Firework:
    """烟花类"""
    def __init__(self, x, y):
        # 初始化烟花的位置
        self.x = x
        self.y = y
        # 爆炸后的粒子列表
        self.particles = []
        # 是否已经爆炸
        self.exploded = False
        # 烟花的上升速度（负值表示向上）
        self.vy = -random.uniform(10, 14)
        # 上升轨迹点列表
        self.trail = []
        # 轨迹的最大长度
        self.max_trail = 12
        # 烟花的颜色（从预定义颜色中随机选择）
        self.color = random.choice(['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F'])
        
    def update(self):
        """更新烟花的状态"""
        if not self.exploded:
            # 如果还没爆炸，处理上升阶段
            # 添加当前位置到轨迹
            self.trail.append((self.x, self.y))
            # 如果轨迹太长，删除最旧的点
            if len(self.trail) > self.max_trail:
                self.trail.pop(0)
            
            # 更新位置：向上移动
            self.y += self.vy
            # 重力影响：上升速度逐渐减小
            self.vy += 0.35
            # 当速度变为正值时（开始下降），触发爆炸
            if self.vy >= 0:
                self.explode()
        else:
            # 如果已经爆炸，更新所有粒子
            for particle in self.particles[:]:
                # 更新粒子状态，如果粒子死亡则移除
                if not particle.update():
                    self.particles.remove(particle)
    
    def explode(self):
        """烟花爆炸，创建爆炸粒子"""
        self.exploded = True
        # 随机选择爆炸模式
        pattern = random.choice(['circle', 'star', 'spiral', 'random'])
        # 随机确定粒子数量
        num_particles = random.randint(25, 40)
        
        if pattern == 'circle':
            # 圆形爆炸模式
            for i in range(num_particles):
                # 计算每个粒子的角度（均匀分布在圆周上）
                angle = (2 * math.pi * i) / num_particles
                # 随机速度
                speed = random.uniform(4, 7)
                # 计算速度分量
                vx = math.cos(angle) * speed
                vy = math.sin(angle) * speed
                # 随机颜色
                color = self.get_particle_color()
                # 随机生命周期
                life = random.randint(40, 60)
                # 创建粒子并添加到列表
                particle = Particle(self.x, self.y, vx, vy, color, life)
                self.particles.append(particle)
        
        elif pattern == 'star':
            # 星形爆炸模式
            for i in range(num_particles):
                angle = (2 * math.pi * i) / num_particles
                speed = random.uniform(3, 6)
                vx = math.cos(angle) * speed
                vy = math.sin(angle) * speed
                color = self.get_particle_color()
                life = random.randint(45, 65)
                particle = Particle(self.x, self.y, vx, vy, color, life)
                self.particles.append(particle)
        
        elif pattern == 'spiral':
            # 螺旋爆炸模式
            for i in range(num_particles):
                angle = (2 * math.pi * i) / num_particles
                # 螺旋模式：速度随角度变化
                speed = random.uniform(2, 5) + (i % 3) * 1.5
                vx = math.cos(angle) * speed
                vy = math.sin(angle) * speed
                color = self.get_particle_color()
                life = random.randint(35, 55)
                particle = Particle(self.x, self.y, vx, vy, color, life)
                self.particles.append(particle)
        
        else:  # random
            # 随机爆炸模式
            for _ in range(num_particles):
                # 完全随机的角度和速度
                angle = random.uniform(0, 2 * math.pi)
                speed = random.uniform(3, 7)
                vx = math.cos(angle) * speed
                vy = math.sin(angle) * speed
                color = self.get_particle_color()
                life = random.randint(30, 50)
                particle = Particle(self.x, self.y, vx, vy, color, life)
                self.particles.append(particle)
    
    def get_particle_color(self):
        """获取粒子的随机颜色"""
        colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#98D8C8', '#F7DC6F']
        return random.choice(colors)

class FireworksApp:
    """烟花应用程序主类"""
    def __init__(self, root):
        # 保存主窗口引用
        self.root = root
        # 设置窗口标题
        self.root.title("美丽烟花效果")
        # 设置窗口大小
        self.root.geometry("800x600")
        # 设置窗口背景色
        self.root.configure(bg='black')
        
        # 创建画布用于绘制烟花
        self.canvas = tk.Canvas(root, width=800, height=600, bg='black', highlightthickness=0)
        self.canvas.pack(fill='both', expand=True)
        
        # 烟花列表，存储所有活跃的烟花
        self.fireworks = []
        # 程序运行状态标志
        self.running = True
        # 最大烟花数量限制（防止卡顿）
        self.max_fireworks = 6
        
        # 绑定鼠标点击事件
        self.canvas.bind('<Button-1>', self.launch_firework)
        # 绑定空格键事件
        self.root.bind('<space>', self.launch_firework)
        # 绑定ESC键退出事件
        self.root.bind('<Escape>', self.quit_app)
        
        # 在画布上显示操作说明
        self.canvas.create_text(400, 550, text="点击鼠标或按空格发射烟花，ESC退出", 
                               fill='white', font=('Arial', 12))
        
        # 启动游戏主循环
        self.game_loop()
    
    def launch_firework(self, event=None):
        """发射烟花的方法"""
        # 检查是否达到最大烟花数量限制
        if len(self.fireworks) >= self.max_fireworks:
            return
            
        if event:
            # 如果是鼠标点击，使用点击位置
            x = event.x
        else:
            # 如果是空格键，随机选择位置
            x = random.randint(50, 750)
        
        # 创建新烟花并添加到列表
        firework = Firework(x, 580)
        self.fireworks.append(firework)
    
    def quit_app(self, event=None):
        """退出应用程序"""
        self.running = False
        self.root.quit()
    
    def draw_particle_trail(self, particle):
        """绘制粒子的尾迹效果"""
        # 遍历粒子的尾迹点
        for i, (tx, ty) in enumerate(particle.trail):
            # 计算透明度（越旧的尾迹越透明）
            alpha = int((i / len(particle.trail)) * 255)
            # 计算尾迹点的大小（越旧的尾迹越小）
            size = max(1, int((i / len(particle.trail)) * 2.5))
            # 确保尾迹点在屏幕范围内
            if 0 <= tx < 800 and 0 <= ty < 600:
                # 绘制尾迹点
                self.canvas.create_oval(tx-size, ty-size, tx+size, ty+size, 
                                      fill=particle.color, outline=particle.color)
    
    def game_loop(self):
        """游戏主循环"""
        # 检查程序是否还在运行
        if not self.running:
            return
        
        # 清空画布，准备重新绘制
        self.canvas.delete('all')
        
        # 更新和绘制所有烟花
        for firework in self.fireworks[:]:
            # 更新烟花状态
            firework.update()
            
            if not firework.exploded:
                # 如果烟花还没爆炸，绘制上升轨迹
                for i, (tx, ty) in enumerate(firework.trail):
                    # 计算轨迹点的透明度
                    alpha = int((i / len(firework.trail)) * 255)
                    # 计算轨迹点的大小
                    size = max(2, int((i / len(firework.trail)) * 3.5))
                    # 确保轨迹点在屏幕范围内
                    if 0 <= tx < 800 and 0 <= ty < 600:
                        # 绘制轨迹点
                        self.canvas.create_oval(tx-size, ty-size, tx+size, ty+size, 
                                              fill=firework.color, outline=firework.color)
                
                # 绘制烟花本体（带白色边框）
                self.canvas.create_oval(firework.x-3, firework.y-3, firework.x+3, firework.y+3, 
                                      fill=firework.color, outline='white')
            else:
                # 如果烟花已经爆炸，绘制所有爆炸粒子
                for particle in firework.particles:
                    # 绘制粒子的尾迹
                    if len(particle.trail) > 1:
                        self.draw_particle_trail(particle)
                    
                    # 绘制粒子本体
                    # 粒子大小根据生命周期动态变化
                    size = max(1, int(particle.life / particle.max_life * 3))
                    # 确保粒子在屏幕范围内
                    if 0 <= particle.x < 800 and 0 <= particle.y < 600:
                        self.canvas.create_oval(particle.x-size, particle.y-size, 
                                              particle.x+size, particle.y+size, 
                                              fill=particle.color, outline=particle.color)
            
            # 移除已经消失的烟花（爆炸后所有粒子都消失）
            if firework.exploded and not firework.particles:
                self.fireworks.remove(firework)
        
        # 显示当前烟花数量信息
        self.canvas.create_text(400, 550, text=f"烟花数量: {len(self.fireworks)}/{self.max_fireworks}", 
                               fill='white', font=('Arial', 12))
        # 显示操作说明
        self.canvas.create_text(400, 570, text="点击鼠标或按空格发射烟花，ESC退出", 
                               fill='white', font=('Arial', 12))
        
        # 设置下一次循环的延迟时间（控制帧率）
        self.root.after(35, self.game_loop)  # 约28FPS的帧率

def main():
    """程序入口函数"""
    # 创建主窗口
    root = tk.Tk()
    # 创建烟花应用程序实例
    app = FireworksApp(root)
    # 启动主事件循环
    root.mainloop()

# 程序入口点
if __name__ == "__main__":
    main() 